#include "ThreadCache.h"
#include "CentralCache.h"

void *ThreadCache::Allocate(size_t size)
{
    assert(size <= MAX_BYTES);
    // 获得内存对齐数
    size_t alineSize = AlineRule::RoundUp(size);

    // 获得自由链表的对应下标
    size_t index = AlineRule::GetIndex(size);

    // 若链表不为空，则直接从_freeList获取链表（无需加锁）
    if (!_freeLists[index].Empty())
    {
        return _freeLists[index].Pop();
    }
    // 若链表为空，则向CentralCache申请空间
    else
    {
        return FetchFromCentralCache(index, alineSize);
    }
}

void *ThreadCache::FetchFromCentralCache(size_t index, size_t alineSize)
{
    // 采用慢开始的反馈调节算法，先确定批数量
    size_t maxSize = _freeLists[index].GetMaxSize();
    size_t batchNum = std::min(maxSize, AlineRule::BatchNumMax(alineSize));
    if (batchNum == maxSize)
    {
        _freeLists[index].AddMaxSize();
    }

    // 从CentralCache申请一批内存
    void *start = nullptr, *end = nullptr;
    size_t realNum = CentralCache::GetInstance()->FetchRangeObj(start, end, alineSize, batchNum);
    assert(realNum > 0);

    // 将多申请的内存链入freelist
    if (realNum > 1)
    {
        _freeLists[index].PushRange(NextObj(start), end, realNum - 1);
    }

    return start;
}

void ThreadCache::Deallocate(void *ptr, size_t size)
{
    assert(ptr != nullptr && size <= MAX_BYTES);
    // 获得自由链表的对应下标
    size_t index = AlineRule::GetIndex(size);
    _freeLists[index].Push(ptr);

    // 如果该freelist中的内存块数量大于一次批量申请的内存，就将一段内存还给CentralCache
    if (_freeLists[index].Size() >= _freeLists[index].GetMaxSize())
    {
        ListTooLong(_freeLists[index], _freeLists[index].GetMaxSize(), size);
    }
}

void ThreadCache::ListTooLong(FreeList &list, size_t len, size_t size)
{
    // 从list当中取出内存并断开连接关系
    void *start = nullptr;
    void *end = nullptr;
    list.PopRange(start, end, len); // 直接释放一批

    // 将内存段归还CentralCache
    CentralCache::GetInstance()->ReleaseListToSpans(start, size);
}

ThreadCache::~ThreadCache()
{
   for(size_t i = 0; i < NFREELISTS; ++i)
   {
        if(!_freeLists[i].Empty())
        {
            // cout << i << ":" << _freeLists[i].Size() << endl; // debug
            ListTooLong(_freeLists[i], _freeLists[i].Size(), AlineRule::IndexToSize(i));
        }
   }
}